#include "common/pch.h"
#include "vmap/dynamic_map_tree.h"

#if 1
class GameObjectModel;
#endif
#if 0
template<> struct HashTrait< GameObjectModel>
{
    static size_t hashCode(const GameObjectModel& g) { return (size_t)(void*)&g; }
};

template<> struct PositionTrait< GameObjectModel>
{
    static void getPosition(const GameObjectModel& g, Vector3& p) { p = g.GetPosition(); }
};

template<> struct BoundsTrait< GameObjectModel>
{
    static void getBounds(const GameObjectModel& g, G3D::AABox& out) { out = g.GetBounds(); }
    static void getBounds2(const GameObjectModel* g, G3D::AABox& out) { out = g->GetBounds(); }
};
#endif


//int UNBALANCED_TIMES_LIMIT = 5;
int CHECK_TREE_PERIOD = 200;

#if 0
typedef RegularGrid2D<GameObjectModel, BIHWrap<GameObjectModel> > ParentTree;
#endif

#if 1
struct DynTreeImpl
{
#else
struct DynTreeImpl : public ParentTree/*, public Intersectable*/
{
#endif
#if 1
    DynTreeImpl()
    {
    }
#else
    typedef GameObjectModel Model;
    typedef ParentTree base;

    DynTreeImpl() :
        rebalance_timer(CHECK_TREE_PERIOD),
        unbalanced_times(0)
    {
    }
#endif

    void insert(const GameObjectModel& mdl)
    {
#if 0
        base::insert(mdl);
#endif
        ++unbalanced_times;
    }

    void remove(const GameObjectModel& mdl)
    {
#if 0
        base::remove(mdl);
#endif
        ++unbalanced_times;
    }

    void balance()
    {
#if 0
        base::balance();
#endif
        unbalanced_times = 0;
    }

    void update(unsigned int difftime)
    {
#if 0
        if (!size())
        {
            return;
        }
#endif

#if 0
        rebalance_timer.Update(difftime);
#endif
#if 0
        if (rebalance_timer.Passed())
        {
            rebalance_timer.Reset(CHECK_TREE_PERIOD);
            if (unbalanced_times > 0)
            {
                balance();
            }
        }
#endif
    }

#if 0
    TimeTracker rebalance_timer;
#endif
    int unbalanced_times;
};

DynamicMapTree::DynamicMapTree() : impl(*new DynTreeImpl())
{
}

DynamicMapTree::~DynamicMapTree()
{
    delete& impl;
}

void DynamicMapTree::insert(const GameObjectModel& mdl)
{
    impl.insert(mdl);
}

void DynamicMapTree::remove(const GameObjectModel& mdl)
{
    impl.remove(mdl);
}

bool DynamicMapTree::contains(const GameObjectModel& mdl) const
{
#if 1
    return true;
#else
    return impl.contains(mdl);
#endif
}

void DynamicMapTree::balance()
{
    impl.balance();
}

int DynamicMapTree::size() const
{
#if 1
    return 0;
#else
    return impl.size();
#endif
}

void DynamicMapTree::update(unsigned int t_diff)
{
    impl.update(t_diff);
}

#if 0
struct DynamicTreeIntersectionCallback
{
    bool did_hit;
    DynamicTreeIntersectionCallback() : did_hit(false) {}
    bool operator()(const G3D::Ray& r, const GameObjectModel& obj, float& distance)
    {
        did_hit = obj.IntersectRay(r, distance, true);
        return did_hit;
    }
    bool didHit() const { return did_hit; }
};

struct DynamicTreeIntersectionCallback_WithLogger
{
    bool did_hit;
    DynamicTreeIntersectionCallback_WithLogger() : did_hit(false)
    {
        DEBUG_LOG("Dynamic Intersection log");
    }
    bool operator()(const G3D::Ray& r, const GameObjectModel& obj, float& distance)
    {
        DEBUG_LOG("testing intersection with %s", obj.GetName().c_str());
        bool hit = obj.IntersectRay(r, distance, true);
        if (hit)
        {
            did_hit = true;
            DEBUG_LOG("result: intersects");
        }
        return hit;
    }
    bool didHit() const { return did_hit; }
};
#endif

//=========================================================
/**
If intersection is found within pMaxDist, sets pMaxDist to intersection distance and returns true.
Else, pMaxDist is not modified and returns false;
*/

bool DynamicMapTree::getIntersectionTime(const G3D::Ray& ray, const G3D::Vector3& endPos, float& pMaxDist) const
{
#if 1
    return true;
#else
    float distance = pMaxDist;
    DynamicTreeIntersectionCallback callback;
    impl.intersectRay(ray, callback, distance, endPos);
    if (callback.didHit())
    {
        pMaxDist = distance;
    }
    return callback.didHit();
#endif
}
//=========================================================

bool DynamicMapTree::getObjectHitPos(float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float pModifyDist) const
{
#if 1
    return true;
#else
    Vector3 pos1 = Vector3(x1, y1, z1);
    Vector3 pos2 = Vector3(x2, y2, z2);
    Vector3 resultPos;
    bool result = getObjectHitPos(pos1, pos2, resultPos, pModifyDist);
    rx = resultPos.x;
    ry = resultPos.y;
    rz = resultPos.z;
    return result;
#endif
}

//=========================================================
/**
When moving from pos1 to pos2 check if we hit an object. Return true and the position if we hit one
Return the hit pos or the original dest pos
*/
bool DynamicMapTree::getObjectHitPos(const G3D::Vector3& pPos1, const G3D::Vector3& pPos2, G3D::Vector3& pResultHitPos, float pModifyDist) const
{
#if 1
    return true;
#else
    bool result = false;
    float maxDist = (pPos2 - pPos1).magnitude();
    // valid map coords should *never ever* produce float overflow, but this would produce NaNs too:
    CFT_ASSERT(maxDist < std::numeric_limits<float>::max());
    // prevent NaN values which can cause BIH intersection to enter infinite loop
    if (maxDist < 1e-10f)
    {
        pResultHitPos = pPos2;
        return false;
    }
    Vector3 dir = (pPos2 - pPos1) / maxDist;            // direction with length of 1
    G3D::Ray ray(pPos1, dir);
    float dist = maxDist;
    if (getIntersectionTime(ray, pPos2, dist))
    {
        pResultHitPos = pPos1 + dir * dist;
        if (pModifyDist < 0)
        {
            if ((pResultHitPos - pPos1).magnitude() > -pModifyDist)
            {
                pResultHitPos = pResultHitPos + dir * pModifyDist;
            }
            else
            {
                pResultHitPos = pPos1;
            }
        }
        else
        {
            pResultHitPos = pResultHitPos + dir * pModifyDist;
        }
        result = true;
    }
    else
    {
        pResultHitPos = pPos2;
        result = false;
    }
    return result;
#endif
}

bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2) const
{
#if 1
    return true;
#else
    G3D::Vector3 v1(x1, y1, z1), v2(x2, y2, z2);

    float maxDist = (v2 - v1).magnitude();

    if (!G3D::fuzzyGt(maxDist, 0))
    {
        return true;
    }

    G3D::Ray r(v1, (v2 - v1) / maxDist);
    DynamicTreeIntersectionCallback callback;
    impl.intersectRay(r, callback, maxDist, v2);

    return !callback.did_hit;
#endif
}

float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist) const
{
#if 1
    return 0;
#else
    G3D::Vector3 v(x, y, z);
    G3D::Ray r(v, Vector3(0, 0, -1));
    DynamicTreeIntersectionCallback callback;
    impl.intersectZAllignedRay(r, callback, maxSearchDist);

    if (callback.didHit())
    {
        return v.z - maxSearchDist;
    }
    else
    {
        return -G3D::inf();
    }
#endif
}
